package com.ucode.hrm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.NumberChineseFormatter;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.ucode.hrm.handle.FddAPIHandler;
import com.ucode.hrm.mapper.HrmCompanyMapper;
import com.ucode.hrm.mapper.HrmContractTplMapper;
import com.ucode.hrm.mapper.HrmEmployeeContractMapper;
import com.ucode.hrm.mapper.HrmEmployeeMapper;
import com.ucode.hrm.service.HrmEmployeeContractService;
import com.ucode.model.hrm.HrmCompany;
import com.ucode.model.hrm.HrmContractTpl;
import com.ucode.model.hrm.HrmEmployee;
import com.ucode.model.hrm.HrmEmployeeContract;
import com.ucode.model.hrm.enums.HrmEmployeeContractStatusEnum;
import com.ucode.model.hrm.enums.HrmResignationTypeEnum;
import com.ucode.model.hrm.vo.HrmGenerateContractParams;
import com.ucode.tool.base.enums.EducationLevelEnum;
import com.ucode.tool.base.enums.GlobalResultCodeEnum;
import com.ucode.tool.base.enums.SexEnum;
import com.ucode.tool.exception.UcodeServiceException;
import com.ucode.tool.util.IdentifyUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.List;
import java.util.Map;

@Service
public class HrmEmployeeContractServiceImpl implements HrmEmployeeContractService {

    private static Log log = LogFactory.get();

    @Resource
    private HrmEmployeeContractMapper hrmEmployeeContractMapper;
    @Resource
    private FddAPIHandler fddAPIHandler;
    @Resource
    private HrmContractTplMapper contractTplMapper;
    @Resource
    private HrmCompanyMapper companyMapper;
    @Resource
    private HrmEmployeeMapper hrmEmployeeMapper;

    @Override
    public HrmEmployeeContractMapper initMapper() {
        return hrmEmployeeContractMapper;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insert(HrmEmployeeContract hrmEmployeeContract) {
        if(ObjUtil.isNull(hrmEmployeeContract.getId()))
            hrmEmployeeContract.setId(IdentifyUtils.getDefaultSnowflakeId());

        hrmEmployeeContractMapper.insert(hrmEmployeeContract);
        return hrmEmployeeContract.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateThreeContract(Long id,Long employeeId) {
        HrmEmployeeContract hrmEmployeeContract = this.validate(id);
        if(ObjUtil.isNotNull(employeeId) && !ObjUtil.equals(employeeId,hrmEmployeeContract.getEmployeeId()))
            throw new UcodeServiceException(GlobalResultCodeEnum.VALIDATE_FAILED);

        HrmContractTpl contractTpl = contractTplMapper.selectByPrimaryKey(hrmEmployeeContract.getTemplateId());
        if(ObjUtil.isNull(contractTpl))
            throw new UcodeServiceException(GlobalResultCodeEnum.VALIDATE_FAILED);

        HrmEmployeeContract updateBean = new HrmEmployeeContract();
        updateBean.setId(id);

        if(StrUtil.isBlank(hrmEmployeeContract.getViewpdfUrl())){

            //先获取PDF模板表单域
            List<String> templateKeys = fddAPIHandler.getTemplateKeys(contractTpl.getThreeTemplateId());
            templateKeys = ObjUtil.isNull(templateKeys) ? CollUtil.newArrayList() : templateKeys;

            HrmGenerateContractParams paramsMap = new HrmGenerateContractParams();
            if(templateKeys.contains("partyAName"))
                paramsMap.setPartyAName(StrUtil.isBlank(hrmEmployeeContract.getSignCompanyName()) ? "/" : hrmEmployeeContract.getSignCompanyName());

            if(templateKeys.contains("partyAAddress"))
                paramsMap.setPartyAAddress(StrUtil.isBlank(hrmEmployeeContract.getSignCompanyAddress()) ? "/" : hrmEmployeeContract.getSignCompanyAddress());

            if(templateKeys.contains("partyAEmail"))
                paramsMap.setPartyAEmail(StrUtil.isBlank(hrmEmployeeContract.getSignCompanyEmail()) ? "/" : hrmEmployeeContract.getSignCompanyEmail());

            if(templateKeys.contains("partyBName"))
                paramsMap.setPartyBName(StrUtil.isBlank(hrmEmployeeContract.getEmployeeName()) ? "/" : hrmEmployeeContract.getEmployeeName());
            if(templateKeys.contains("partyBAddress")){
                paramsMap.setPartyBAddress(StrUtil.isBlank(hrmEmployeeContract.getEmployeeAddress()) ? "/" : hrmEmployeeContract.getEmployeeAddress());
            }
            if(templateKeys.contains("partyBMobile"))
                paramsMap.setPartyBMobile(StrUtil.isBlank(hrmEmployeeContract.getEmployeeMobile()) ? "/" : hrmEmployeeContract.getEmployeeMobile());

            if(templateKeys.contains("partyBEmail"))
                paramsMap.setPartyBEmail(StrUtil.isBlank(hrmEmployeeContract.getEmployeeEmail()) ? "/" : hrmEmployeeContract.getEmployeeEmail());

            if(templateKeys.contains("partyBIdCard"))
                paramsMap.setPartyBIdCard(StrUtil.isBlank(hrmEmployeeContract.getEmployeeIdCard()) ? "/" : hrmEmployeeContract.getEmployeeIdCard());

            if(templateKeys.contains("partyBMobile2"))
                paramsMap.setPartyBMobile2(StrUtil.isBlank(hrmEmployeeContract.getEmployeeMobile2()) ? "/" : hrmEmployeeContract.getEmployeeMobile2());

            if(templateKeys.contains("startTimeC1"))
                paramsMap.setStartTimeC1(String.valueOf(hrmEmployeeContract.getStartDate().getYear()));
            if(templateKeys.contains("startTimeC2"))
                paramsMap.setStartTimeC2(String.format("%02d",hrmEmployeeContract.getStartDate().getMonthValue()));
            if(templateKeys.contains("startTimeC3"))
                paramsMap.setStartTimeC3(String.format("%02d",hrmEmployeeContract.getStartDate().getDayOfMonth()));

            if(templateKeys.contains("endTimeC1"))
                paramsMap.setEndTimeC1(String.valueOf(hrmEmployeeContract.getEndDate().getYear()));
            if(templateKeys.contains("endTimeC2"))
                paramsMap.setEndTimeC2(String.format("%02d",hrmEmployeeContract.getEndDate().getMonthValue()));
            if(templateKeys.contains("endTimeC3"))
                paramsMap.setEndTimeC3(String.format("%02d",hrmEmployeeContract.getEndDate().getDayOfMonth()));

            if(ObjUtil.isNotNull(hrmEmployeeContract.getTrialEndDate()) && templateKeys.contains("probationPeriod")){

                // 计算月份差（自动处理跨年），不足一个月按照一个月算
                //Period period = Period.between(hrmEmployeeContract.getTrialEndDate(), hrmEmployeeContract.getTrialEndDate().plusDays(1)); // 包含截止日
                //int totalMonths = period.getYears() * 12 + period.getMonths();
                //int monthsBetwee = (period.getDays() > 0) ? totalMonths + 1 : totalMonths; // 不足整月+1

                // 处理负值（确保正数结果）
                int probationPeriod = calculateTrialPeriodMonths(hrmEmployeeContract.getStartDate(),hrmEmployeeContract.getTrialEndDate());
                paramsMap.setProbationPeriod(String.valueOf(probationPeriod));
                paramsMap.setStartTimeP1(paramsMap.getStartTimeC1());
                paramsMap.setStartTimeP2(paramsMap.getStartTimeC2());
                paramsMap.setStartTimeP3(paramsMap.getStartTimeC3());

                paramsMap.setEndTimeP1(String.valueOf(hrmEmployeeContract.getTrialEndDate().getYear()));
                paramsMap.setEndTimeP2(String.format("%02d",hrmEmployeeContract.getTrialEndDate().getMonthValue()));
                paramsMap.setEndTimeP3(String.format("%02d",hrmEmployeeContract.getTrialEndDate().getDayOfMonth()));
            }

            if(templateKeys.contains("salary"))
                paramsMap.setSalary(ObjUtil.isNull(hrmEmployeeContract.getBaseSalary()) || hrmEmployeeContract.getBaseSalary().compareTo(BigDecimal.ZERO) == 0 ? "/" : hrmEmployeeContract.getBaseSalary().toString());

            if(templateKeys.contains("hzSalary")){
                if(ObjUtil.isNull(hrmEmployeeContract.getBaseSalary()) || hrmEmployeeContract.getBaseSalary().compareTo(BigDecimal.ZERO) == 0){
                    paramsMap.setHzSalary("/");
                }else {
                    paramsMap.setHzSalary(NumberChineseFormatter.format(hrmEmployeeContract.getBaseSalary().doubleValue(),false,true,"负","元"));
                }
            }

            if(templateKeys.contains("paySalaryDay")){
                paramsMap.setPaySalaryDay(StrUtil.isNotBlank(hrmEmployeeContract.getPaySalaryDay()) ? hrmEmployeeContract.getPaySalaryDay() : "/");
            }

            if(templateKeys.contains("customCompany"))
                paramsMap.setCustomCompany(hrmEmployeeContract.getCustomCompany());

            if(templateKeys.contains("deptName"))
                paramsMap.setDeptName(StrUtil.isBlank(hrmEmployeeContract.getDeptName()) ? "/" : hrmEmployeeContract.getDeptName());

            if(templateKeys.contains("jobName"))
                paramsMap.setJobName(StrUtil.isBlank(hrmEmployeeContract.getJobName()) ? "/" : hrmEmployeeContract.getJobName());

            if(templateKeys.contains("workLocation"))
                paramsMap.setWorkLocation(StrUtil.isBlank(hrmEmployeeContract.getWorkLocation()) ? "/" : hrmEmployeeContract.getWorkLocation());

            HrmEmployee hrmEmployee = hrmEmployeeMapper.selectByPrimaryKey(employeeId);
            if(templateKeys.contains("gender")){
                SexEnum sexEnum = SexEnum.get(hrmEmployee.getGender());
                paramsMap.setGender(ObjUtil.isNotNull(sexEnum) ? sexEnum.getMessage() : "未知");
            }

            if(templateKeys.contains("registeredResidence")){
                paramsMap.setRegisteredResidence(StrUtil.isBlank(hrmEmployee.getRegisteredResidence())? IdcardUtil.getProvinceByIdCard(hrmEmployee.getIdCardNumber()) : hrmEmployee.getRegisteredResidence());
            }

            if(templateKeys.contains("detailedAddress")){
                paramsMap.setDetailedAddress(StrUtil.isNotBlank(hrmEmployee.getResidentialAddress()) ? hrmEmployee.getResidentialAddress() : hrmEmployee.getDetailedAddress());
            }

            if(templateKeys.contains("residentialAddress")){
                paramsMap.setResidentialAddress(StrUtil.isNotBlank(hrmEmployee.getDetailedAddress()) ? hrmEmployee.getDetailedAddress() : hrmEmployee.getResidentialAddress());
            }

            if(templateKeys.contains("education")){
                EducationLevelEnum educationLevelEnum = EducationLevelEnum.get(hrmEmployee.getEducation());
                paramsMap.setEducation(ObjUtil.isNotNull(educationLevelEnum) ? educationLevelEnum.getMessage() : "未知");
            }


            if(templateKeys.contains("resignationDate")){
                paramsMap.setResignationDate(ObjUtil.isNotNull(hrmEmployee.getResignationDate()) ? LocalDateTimeUtil.format(hrmEmployee.getResignationDate(),"yyyy-MM-dd") : "/");
            }

            if(templateKeys.contains("resignReason")){
                paramsMap.setResignReason(ObjUtil.isNotNull(hrmEmployee.getResigType()) && ObjUtil.isNotNull(HrmResignationTypeEnum.get(hrmEmployee.getResigType())) ? HrmResignationTypeEnum.get(hrmEmployee.getResigType()).getMessage() : "/");
            }

            if(templateKeys.contains("economic")){
                paramsMap.setEconomic(ObjUtil.isNotNull(hrmEmployee.getEconomic()) && hrmEmployee.getEconomic() ? "是" : "否");
            }

            if(templateKeys.contains("compensation")){
                paramsMap.setCompensation(ObjUtil.isNotNull(hrmEmployee.getCompensation()) && hrmEmployee.getCompensation().compareTo(BigDecimal.ZERO) != 0 ? hrmEmployee.getCompensation().toString() : "/");
            }

            if(templateKeys.contains("comFeeTotal")){
                paramsMap.setComFeeTotal(ObjUtil.isNotNull(hrmEmployee.getComFeeTotal()) && hrmEmployee.getComFeeTotal().compareTo(BigDecimal.ZERO) != 0 ? hrmEmployee.getComFeeTotal().toString() : "/");
            }

            if(templateKeys.contains("comProvideTime")){
                paramsMap.setComProvideTime(ObjUtil.isNotNull(hrmEmployee.getComProvideTime()) ? LocalDateTimeUtil.format(hrmEmployee.getComProvideTime(),"yyyy-MM-dd") : "/");
            }

            //在线模板的时候需要调用下面的方法，否则会报参数错误
            //fddAPIHandler.addContractByTplKeys(hrmEmployeeContract.getThreeTemplateId());

            //生成合同
            Map<String,String> result = fddAPIHandler.generateContractByTpl(contractTpl.getThreeTemplateId(),hrmEmployeeContract.getName(),hrmEmployeeContract.getId().toString(),paramsMap);
            String download_url = result.get("download_url");//合同下载地址
            String viewpdf_url = result.get("viewpdf_url");//合同预览地址

            updateBean.setDownloadUrl(download_url);
            updateBean.setViewpdfUrl(viewpdf_url);

        }

        String customerId = fddAPIHandler.registerAccount("1",hrmEmployeeContract.getEmployeeId().toString());
        //生成员工签署合同地址
        String transactionId = "US"+IdentifyUtils.getDefaultSnowflakeId();
        String signKeyword = contractTpl.getSignKeyword2();
        String keywordStrategy = contractTpl.getKeywordStrategy2();
        String extSignUrl = fddAPIHandler.personVerifySign(transactionId,hrmEmployeeContract.getId().toString(),customerId,signKeyword,keywordStrategy);

        updateBean.setExtSignUrl(extSignUrl);
        hrmEmployeeContractMapper.update(updateBean);

        HrmEmployee hrmEmployee = new HrmEmployee();
        hrmEmployee.setId(hrmEmployeeContract.getEmployeeId());
        hrmEmployee.setContractStatus(HrmEmployeeContractStatusEnum.EMPLOYEE_PENDING.getCode());
        hrmEmployee.setContractType(hrmEmployeeContract.getContractType());
        hrmEmployee.setContractId(hrmEmployeeContract.getId());
        hrmEmployeeMapper.update(hrmEmployee);

        return extSignUrl;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void signNotify(String transactionId, Long contractId, String downloadUrl, String viewpdfUrl) {


        if(StrUtil.isNotBlank(downloadUrl)){
            downloadUrl = fddAPIHandler.urlConversionByProd(downloadUrl);
        }

        if(StrUtil.isNotBlank(viewpdfUrl)){
            viewpdfUrl = fddAPIHandler.urlConversionByProd(viewpdfUrl);
        }

        HrmEmployeeContract hrmEmployeeContract = this.validate(contractId);
        //员工签约
        if(transactionId.startsWith("US")){

            HrmEmployeeContract updateBean = new HrmEmployeeContract();
            updateBean.setId(contractId);
            updateBean.setDownloadUrl(downloadUrl);
            updateBean.setViewpdfUrl(viewpdfUrl);
            updateBean.setStatus(HrmEmployeeContractStatusEnum.COMPANY_PENDING.getCode());
            updateBean.setSignDate(LocalDateTime.now());
            hrmEmployeeContractMapper.update(updateBean);

            HrmEmployee hrmEmployee = new HrmEmployee();
            hrmEmployee.setId(hrmEmployeeContract.getEmployeeId());
            hrmEmployee.setContractStatus(HrmEmployeeContractStatusEnum.COMPANY_PENDING.getCode());
            hrmEmployee.setContractType(hrmEmployeeContract.getContractType());
            hrmEmployeeMapper.update(hrmEmployee);

            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

                @Override
                public void afterCompletion(int status) {
                    //事物成功提交后
                    if (status == TransactionSynchronization.STATUS_COMMITTED) {

                        //异步完成公司侧签约，预防长时间执行后，多次回调的问题
                        new Thread(() -> {
                            HrmCompany company = companyMapper.selectByPrimaryKey(hrmEmployeeContract.getSignCompanyId());
                            if(ObjUtil.isNull(company))
                                throw new UcodeServiceException(GlobalResultCodeEnum.VALIDATE_FAILED);

                            //骑缝章自动签
                            String transactionId = "DACS"+IdentifyUtils.getDefaultSnowflakeId();
                            fddAPIHandler.docusignAcrossPage(transactionId,hrmEmployeeContract.getId().toString(),FddAPIHandler.customerId,company.getThreeStorageId());

                            HrmContractTpl contractTpl = contractTplMapper.selectByPrimaryKey(hrmEmployeeContract.getTemplateId());
                            if(ObjUtil.isNull(contractTpl))
                                throw new UcodeServiceException(GlobalResultCodeEnum.VALIDATE_FAILED);

                            //公司侧自动签署
                            transactionId = "CS"+IdentifyUtils.getDefaultSnowflakeId();
                            String signKeyword = contractTpl.getSignKeyword1();
                            String keywordStrategy = contractTpl.getKeywordStrategy1();
                            String signatureId = company.getThreeStorageId();
                            Map<String,String> data = fddAPIHandler.extSignAutoContract(transactionId,hrmEmployeeContract.getName(),hrmEmployeeContract.getId().toString(),signKeyword,keywordStrategy,signatureId);

                            String download_url = data.get("download_url");//合同下载地址
                            String viewpdf_url = data.get("viewpdf_url");//合同预览地址

                            getSelf().signNotify(transactionId,contractId,download_url,viewpdf_url);

                        }).start();

                    }else if(status == TransactionSynchronization.STATUS_ROLLED_BACK){
                        //事物回滚后
                    }
                }

            });

        }else if(transactionId.startsWith("DACS")){
            log.info("骑缝章自动签签署结果......");
        }else if(transactionId.startsWith("CS")){
            HrmEmployeeContract updateBean = new HrmEmployeeContract();
            updateBean.setId(contractId);
            updateBean.setDownloadUrl(downloadUrl);
            updateBean.setViewpdfUrl(viewpdfUrl);
            updateBean.setStatus(HrmEmployeeContractStatusEnum.COMPLETED_SIGNED.getCode());
            updateBean.setSignDate(LocalDateTime.now());
            hrmEmployeeContractMapper.update(updateBean);

            HrmEmployee hrmEmployee = new HrmEmployee();
            hrmEmployee.setId(hrmEmployeeContract.getEmployeeId());
            hrmEmployee.setContractStatus(HrmEmployeeContractStatusEnum.COMPLETED_SIGNED.getCode());
            hrmEmployee.setContractType(hrmEmployeeContract.getContractType());
            hrmEmployee.setContractId(hrmEmployeeContract.getId());
            hrmEmployeeMapper.update(hrmEmployee);

        }else if(transactionId.startsWith("AAS")){

            log.info("授权自动签签署结果......");

        }
    }

    @Override
    public int updateVisibleByIds(Collection<Long> ids, Boolean visible) {
        return hrmEmployeeContractMapper.updateVisibleByIds(ids,visible);
    }

    /**
     * 计算两个日期相差的月份，不足一月，按照1月算
     * @param startDate
     * @param endDate
     * @return
     */
    public static int calculateTrialPeriodMonths(LocalDate startDate, LocalDate endDate) {
        // 处理日期为空的情况
        if (startDate == null || endDate == null) {
            return 0;
        }

        // 交换日期如果开始日期在结束日期之后
        if (startDate.isAfter(endDate)) {
            LocalDate temp = startDate;
            startDate = endDate;
            endDate = temp;
        }

        // 计算完整月份
        long totalMonths = ChronoUnit.MONTHS.between(
                startDate.withDayOfMonth(1),
                endDate.withDayOfMonth(1)
        );

        // 计算剩余天数
        LocalDate calculatedEnd = startDate.plusMonths(totalMonths);
        int remainingDays = (int) ChronoUnit.DAYS.between(calculatedEnd, endDate);

        // 如果有剩余天数，增加一个月
        if (remainingDays > 0) {
            totalMonths++;
        }

        return (int) totalMonths;
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private HrmEmployeeContractServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }
}
